package com.fileupload.fileupload.utils;

import io.minio.*;
import io.minio.errors.*;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.FileUtils;

import java.io.*;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;

@Slf4j
public class MinioFileUtils {
    private static final int chunkSize = 1024 * 1024 * 5; // 5MB 分片大小，可根据实际调整

    public static String uploadFileMinio(MinioClient minioClient, String bucketName, String tempBucketName, String filePath)
            throws ServerException, InsufficientDataException, ErrorResponseException, IOException,
            NoSuchAlgorithmException, InvalidKeyException, InvalidResponseException, XmlParserException, InternalException {
        String result = null;
        File file = new File(filePath);
        if (!file.exists()){
            log.error("文件不存在");
            throw new RuntimeException("从数据库导出的excel文件不存在！");
        }
        if (file.length() > chunkSize) {
            // 文件大于1MB，执行分片上传
            result = uploadFileInChunksAndMergeStream(minioClient, bucketName, tempBucketName, file);
        }
        else {
            // 直接上传整个文件minio中
            result = uploadMinio(minioClient, bucketName, file);
        }
        log.info("文件上传成功，文件路径：" + result);
        // 删除本地临时文件
        FileUtils.deleteQuietly(file);
        return result;
    }

    private static String uploadMinio(MinioClient minioClient, String bucketName, File file)
            throws IOException, ServerException, InsufficientDataException, ErrorResponseException,
            NoSuchAlgorithmException, InvalidKeyException, InvalidResponseException, XmlParserException, InternalException {
        // 获取文件名和文件的后缀
        String objectName = System.currentTimeMillis() + "/" + file.getName();
        // 执行上传操作
        minioClient.putObject(PutObjectArgs.builder()
                .bucket(bucketName)
                .object(objectName)
                .stream(new FileInputStream(file), file.length(), -1)
                .build());
        log.info("文件成功上传到minio中,文件名为：{}", objectName);
        return bucketName + "/" + objectName;
    }

    private static String uploadFileInChunksAndMergeStream(MinioClient minioClient, String bucketName, String tempBucketName, File file)
            throws IOException, ServerException, InsufficientDataException, ErrorResponseException,
            NoSuchAlgorithmException, InvalidKeyException, InvalidResponseException, XmlParserException, InternalException {
        String fileName = file.getName();
        String[] split = fileName.split("\\.");
        String objectName = split[0];
        long fileSize = file.length();
        int chunkCount = (int) Math.ceil((double) fileSize / chunkSize);
        log.info("文件大小为：{}，分片数量为：{}", fileSize, chunkCount);
        // 存储分片名称的列表
        List<String> partNames = new ArrayList<>();
        // 逐个上传分片
        for (int i = 0; i < chunkCount; i++) {
            long start = i * chunkSize;
            long end = Math.min(start + chunkSize, fileSize);
            // 读取分片数据
            try (InputStream inputStream = new FileInputStream(file)) {
                inputStream.skip(start);
                byte[] buffer = new byte[(int) (end - start)];
                int bytesRead = 0;  // 初始化 bytesRead 为 0
                // 读取实际的数据量
                bytesRead = inputStream.read(buffer, 0, (int) (end - start));
                // 检查是否到达文件末尾
                if (bytesRead == -1) {
                    break;  // 文件已经读取完毕，退出循环
                }
                // 上传分片到临时桶 , 使用 ByteArrayInputStream 处理读取后的缓冲区
                try (ByteArrayInputStream bais = new ByteArrayInputStream(buffer, 0, bytesRead)) {
                    // 上传分片
                    minioClient.putObject(
                            PutObjectArgs.builder()
                                    .bucket(tempBucketName)
                                    .object(objectName + "-part" + i)
                                    .stream(bais, bytesRead, -1)
                                    .build());
                    partNames.add(objectName + "-part" + i);
                }
                log.info("分片上传成功，分片名称为：{} 上传完毕！", objectName + "-part" + i);
            }
        }
        // 流式合并分片
        String returnUrl = mergeChunksStream(minioClient, bucketName, tempBucketName, System.currentTimeMillis() + "/" + fileName, partNames);
        log.info("文件成功上传到minio中,需要返回前端的路径为：{}", returnUrl);
        return returnUrl;
    }

    private static String mergeChunksStream(MinioClient minioClient, String bucketName,String tempBucketName, String objectName, List<String> partNames)
            throws IOException, ServerException, InsufficientDataException, ErrorResponseException,
            NoSuchAlgorithmException, InvalidKeyException, InvalidResponseException, XmlParserException, InternalException {
        // 构建 ComposeObject 请求
        List<ComposeSource> sourceList = partNames.stream().map(partName -> ComposeSource.builder()
                .bucket(tempBucketName)
                .object(partName)
                .build()).collect(Collectors.toList());
        // 执行合并操作
        minioClient.composeObject(
                ComposeObjectArgs.builder()
                        .bucket(bucketName)
                        .object(objectName)
                        .sources(sourceList)
                        .build());
        log.info("分片合并成功，合并后的文件名为：{}", objectName);
        // 删除临时文件
        for (String partName : partNames) {
            minioClient.removeObject(RemoveObjectArgs.builder().bucket(tempBucketName).object(partName).build());
        }
        return bucketName + "/" + objectName;
    }

}
